/*
 * File:    FSBlock.h
 * Authors: Diane, Dan, and Tim
 * Class:   Operating Systems, Lab 2, UMD
 * Created: September 2012
 *
 */

#ifndef _FSBLOCK_H
#define	_FSBLOCK_H

#include <stdio.h>
#include <stdlib.h>
#include "Disk.h"

#include "CannotReadException.h"
#include "BadNumberOfBlocksException.h"

/**
 * A representation of a Block of data on disk, including a disk address
 * (block number) for the block. Every Block represents a fixed size buffer
 * (e.g., 512 bytes) of RAM data called the "m_buffer". Blocks typically
 * contain some int values (4 byte integer) values in some of their m_buffer
 * locations. E.g., a block can contain a block number-- an address (of the
 * next block on Disk in a linked organization of disk blocks) called the
 * "next pointer". The methods SetInt and GetInt are used to maintain
 * these values.
 *
 * Note: In this class we sometimes call a disk address a "pointer" even
 * though these "pointers" are not references to RAM memory, but
 * references to locations (blocks) on disk.
 */
class FSBlock {
    public:
        /**
         * Creates a RAM representation of a block of data. Doesn't do any error
         * checking. Allocates a new RAM byte array m_buffer, and initializes the
         * buffer of data (data member, m_buffer) to zeros.
         * Does not alter the disk.
         *
         * @param blockNumber should range betwen 0 and
         * (1 less than the number of blocks on the Disk).
         * This is used to set a data member for the Block.
         *
         * @param length should be the same as the block size on the Disk.
         * Allocates the m_buffer with this length.
         */
        FSBlock(int blockNumber, int length);

        /**
         * Deallocates the m_buffer for the Block.
         * TODO: Ensure m_buffer is _NOT_ global.
         */
        ~FSBlock();

        /**
         * Creates a Block object in RAM. Initializes the m_buffer of data to zeros.
         * The block number data member is set to 0. Does not alter the disk.
         *
         * @param length is the number of bytes to allocate for the m_buffer.
         */
        FSBlock(int length);


        /** Attempts to read a FSBlock from a Disk.
         * If the block as written to Disk had int value(s)
         * (e.g., established by SetInt()), those int value(s) will be available
         * after use of this constructor by calling GetInt().
         *
         * FIXME: numberOfBlocks() looks like a function call. However,
         * everything else looks like it is a private unsigned int.
         *
         * @param blockNumber The block number of the FSBlock to read.
         * This can range between 0 and numberOfBlocks()-1 (inclusive)
         * for this Disk object.
         *
         * @param disk is the Disk to read from.
         *
         * @throw BlockException when the blockNumber is invalid,
         * when there is an I/O error, or when the m_buffer is NULL.
         */
        FSBlock(unsigned int blockNumber, Disk * disk);


        /** Set all bytes of the m_buffer of this Block to 0.
         * Does not alter the disk.
         */
        void ClearBuffer();

        /** Returns the block number for this FSBlock, returns value of a data
         * member. Does not access Disk.
         */
        unsigned int GetBlockNumber();

        /**
         * Return the number of bytes in the buffer for this block.
         * This is the block size of this block.
         */
        unsigned int GetBlockSize();

        /**
         * Obtain a reference to the m_buffer data member for this FSBlock.
         * @return a reference to (not a copy of) the byte buffer for this Block.
         */
        unsigned char * GetBuffer();

        /** Retrieves one of the int's in the m_buffer as set by SetInt.
         * @param arrayIndex gives the relative 4 byte location to access. See SetInt.
         */
        int GetInt(int arrayIndex);


        /** Print out the contents of the FSBlock. Prints the m_buffer bytes in hex
         * and decimal representation. Prints out the values of the
         * other data members.
         * For debugging.
         */
        void Print();

        /** Changes the block number data member for this FSBlock RAM object.
         * Doesn't write the change to disk. The block number is not represented by
         * the m_buffer, but rather it is represented as a data member of the
         * FSBlock. The block number is the location where you just read the block
         * from on Disk, or the location where you intend to write the FSBlock
         * to disk.
         */
        void SetBlockNumber(unsigned int number);

        /** Changes the reference of the m_buffer. No copy is made! Memory for the
         * original m_buffer is first deallocated by this method (using "delete").
         *
         * @param buffer is used by reference--a copy is not made.
         * Do not deallocate the buffer passed to this call after the call.
         *
         */
        void SetBuffer(unsigned char *buffer);

        /** Enables the caller to treat the first bytes of the m_buffer as an array
         * of int's. Changes one of these int's in the m_buffer.
         * If arrayIndex=0, the first 4 bytes of the m_buffer are changed to the
         * binary value in the intValue parameter ("first integer").
         * If arrayIndex=1, the second 4 bytes are changed, etc.
         * ("second integer"). Etc. The integers are stored in network byte order
         * (a.k.a., big endian byte order).
         *
         * @param intValue gives the integer.
         * @param arrayIndex gives the location (in terms of 4 byte ints) in the m_buffer to change.
         */
        void SetInt(int intValue, int arrayIndex);

        /** Writes the data (m_buffer) from the FSBlock to its block number on the
         * Disk. The FSBlock must have the correct information. E.g., this can be
         * the case because a previous Block constructor reading from a Disk
         * returned the FSBlock.
         *
         * @param disk The Disk to write this FSBlock to.
         *
         * @return TRUE iff the call succeeded in writing the FSBlock to the Disk.
         */
        bool Write(Disk *disk);

        /** TODO: Add documentation
         */
        static void Debug(bool state);

        /**
         * @TODO
         */
        void Test(Disk *disk);

    private:
        /** number of blocks
         * FIXME: This was not in cprince's dOxygen,
         * and it does not make sense here. FSBlock is a single block, what
         * business does knowing some "number of blocks" have it?
         * @deprecated
         */
        unsigned int numberOfBlocks;
        /// What block number we're stored on in the Disk.
        unsigned int m_blockNumber;

        unsigned int fsblock_length;

        /// This FSBlock's buffer.
        unsigned char *m_buffer;

    /**
    * C++ version 0.4 char* style "itoa":
    * Written by Lukás Chmela
    * Released under GPLv3.
    * @deprecated We don't actually use this.
    * @param value
    */
    char* itoa(int value, char* result, int base);
    };

#endif	/* _FSBlock_H */

